module ecs {
    /**
     * 集合类型有点像Array，但不保留其实体的顺序，speedwise非常好，尤其适合于游戏。
     */
    export class Bag<E> implements ImmutableBag<E> {
        /** 支持数组 */
        private _data: E[];
        /** 包中所含元素的数量。 */
        protected _size = 0;

        /**
         * 构造一个具有指定初始容量的空包
         * @param type 
         * @param capacity 包的初始容量
         */
        constructor(capacity: number = 64) {
            this._data = new Array<E>(capacity);
        }

        /**
         * 返回Bag中指定位置的元素。
         * @param index 要返回的元素的索引
         */
        public get(index: number): E {
            return this._data[index];
        }

        /**
         * 返回此包的底层数组
         */
        public getData(): E[]{
            return this._data;
        }

        /**
         * 在包中的指定索引处设置元素。
         * @param index 
         * @param e 
         */
        public set(index: number, e: E){
            if (index >= this._data.length)
                this.grow(Math.max((2 * this._data.length), index + 1));

            this._size = Math.max(this._size, index + 1);
            this.unsafeSet(index, e);
        }

        /**
         * 检查一件物品，如果在给定的物品上添加，是否能放入袋子中。
         * @param index 
         */
        public ensureCapacity(index: number){
            if (index >= this._data.length)
                this.grow(index + 1);
        }

        /**
         * 
         * @param index 
         * @param e 
         */
        public unsafeSet(index: number, e: E){
            this._data[index] = e;
        }

        /**
         * 返回包可以容纳的元素数量，但不能增加
         */
        public getCapacity(): number{
            return this._data.length;
        }

        /**
         * 返回包中的元素数量。
         */
        public size(): number {
            return this._size;
        }
        /**
         * 如果此包不包含任何元素，则返回true。
         */
        public isEmpty(): boolean {
            return this._size == 0;
        }
        /**
         * 将指定的元素添加到此包的末尾。
         * 
         * 如果需要，它会自动增加袋子的容量。
         * @param e 元素添加到此列表中
         */
        public add(e: E) {
            if (this._size == this._data.length)
                this.grow(this._data.length * 2);

            this._data[this._size++] = e;
        }
        /**
         * 检查bag是否包含此元素。
         * @param e 检查的元素
         */
        public contains(e: E): boolean {
            for (let i = 0; this._size > i; i++) {
                if (e == this._data[i])
                    return true;
            }

            return false;
        }

        private grow(newCapacity: number) {
            let newData = new Array<E>(newCapacity);
            for (let k in this._data)
                newData.push(this._data[k]);

            this._data = newData;
        }
    }
}
